﻿//
// File: MainForm.cs
//
// Purpose:  MainForm and application logic.
//
// Author: MotherHacker @ cachecleaner.codeplex.com


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;


using Reporting;
using QueuedStats = QueuedOperation.QueuedStats;

namespace CacheCleaner
{
    public partial class MainForm : System.Windows.Forms.Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        #region Helpers
        int oldTotalFileCount = 0;
        int totalFileCount = 0;
        int totalDirCount = 0;
        int remainingFileCount = 0;
        int step = 10;

        #region pick step for progressbar
        static readonly int[] stepLimits = new int[] { 10,  50, 100, 200, 500, 1000 };
        private int pickStep(int count)
        {
            for (int i = stepLimits.Length-1; i >= 0; i--)
            {
                if (count >= stepLimits[i])
                    return stepLimits[i];
            }

            return stepLimits[0];
        }
        #endregion

        #region Form_UpdateFileCount
        private void Form_UpdateFileCount(int estfileCount, int step)
        {
            oldTotalFileCount = totalFileCount;
            totalFileCount = estfileCount;
            int fileCount = totalFileCount / 2;

            progressBar.Maximum = totalFileCount;
            progressBar.Step = step;

            if (progressBar.Value < fileCount)
                progressBar.PerformStep();

            this.actionLabel.Text = "Scanning .. " + totalFileCount.ToString() + " files, " + totalDirCount.ToString() + " folders ..";

            Application.DoEvents();
        }

        #endregion

        private string FormatBytes(long bytes)
        {
            if (bytes == 0) return "0 Bytes";

            const int scale = 1024;
            string[] orders = new string[] { "GB", "MB", "KB", "Bytes" };
            long max = (long)Math.Pow(scale, orders.Length - 1);

            foreach (string order in orders)
            {
                if (bytes > max)
                    return string.Format("{0:##.##} {1}", decimal.Divide(bytes, max), order);

                max /= scale;
            }
            return "0 Bytes";
        }
        #endregion

        #region ScanAndDeleteContents
        private void ScanAndDeleteContents(string folder, string searchPattern = "*", bool recursive = false)
        {
            // check if folder path exists
            if (!System.IO.Directory.Exists(folder))
                return;

            Queue<System.IO.FileSystemInfo> childs = new Queue<System.IO.FileSystemInfo>();

            using (FolderScanner scanner = new FolderScanner(folder, searchPattern, SearchOptions.Default))
            {
                foreach (var match in scanner)
                {
                    if (match is System.IO.DirectoryInfo)
                        childs.Enqueue(match);
                    else
                    {
                        totalFileCount++;

                        DeleteOperation op = new DeleteOperation(match);

                        op.Execute(); // will be executed queued

                        step = pickStep(totalFileCount);

                        if (totalFileCount > (oldTotalFileCount + step))
                        {
                            Form_UpdateFileCount(totalFileCount, step);
                        }
                    }
                }
            }

            if (recursive && childs.Count > 0)
            {
                do
                {
                    var match = childs.Dequeue();

                    totalDirCount++;

                    ScanAndDeleteContents(match.FullName, searchPattern, recursive);

                    DeleteOperation op = new DeleteOperation(match);

                    op.Execute(); // will be executed queued

                }
                while (childs.Count > 0);

            }
            else
            {
                totalDirCount += childs.Count;
                childs.Clear();
            }

            Form_UpdateFileCount(totalFileCount, step);

        }

        void QueueForDeletion(System.IO.FileSystemInfo target)
        {
            DeleteOperation delete = new DeleteOperation(target);
            delete.Execute();

            totalFileCount++;
        }

        void QueueForDeletion(string path)
        {
            //if (System.IO.Directory.Exists(path))
            //    QueueForDeletion(new System.IO.DirectoryInfo(path));

            if (System.IO.File.Exists(path))
                QueueForDeletion(new System.IO.FileInfo(path));
        }
        #endregion

        private void cleanNowButton_Click(object sender, EventArgs e)
        {
            DialogResult confirmResult = MessageBox.Show(this, "Make sure that IMVU isnt running, Press Ok to continue", "Confirmation?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
            if (confirmResult == System.Windows.Forms.DialogResult.Cancel)
                return;

            FileLog log = FileLog.CreateNew("errors.log");
            ReportingService.AddListener(log);

            //ErrorListWindow errorListWindow = new ErrorListWindow();
            //ReportingService.AddListener(errorListWindow);
  
            string appData = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            string imvuFolder = System.IO.Path.Combine(appData, "IMVU");

            string tempFolder = System.IO.Path.GetTempPath();

            string root = System.IO.Path.GetPathRoot(appData);
            string driveletter = root.Split(new char[]{System.IO.Path.VolumeSeparatorChar})[0];

            long totalFreeSpace = new System.IO.DriveInfo(driveletter).AvailableFreeSpace;
           

            string imvuAvpicsFolder = imvuFolder + @"\avpics";
            string imvuCacheFolder = imvuFolder + @"\Cache";
            string imvuAssetCacheFolder = imvuFolder + @"\AssetCache";
            string imvuPixmapCacheFolder = imvuFolder + @"\PixmapCache";
            string imvuHttpCacheFolder = imvuFolder + @"\HttpCache";


            // we scan each folder and queue up delete operations for files.
            ScanAndDeleteContents(imvuCacheFolder);

            // imvu content caching folders added in versions 437 onwards.
            ScanAndDeleteContents(imvuAssetCacheFolder);
            ScanAndDeleteContents(imvuPixmapCacheFolder);

            // another cache folder added in versions 446 onwards.
            ScanAndDeleteContents(imvuHttpCacheFolder);

            // avatar picture folder.
            ScanAndDeleteContents(imvuAvpicsFolder);

            // file containing inventory record information.
            string inventoryCacheFile = System.IO.Path.Combine(imvuFolder, "inventory.cache");

            QueueForDeletion(inventoryCacheFile);

            // file containing inventory icon database.
            string iconDatabaseFile = System.IO.Path.Combine(imvuFolder, "productInfoCache.db");

            QueueForDeletion(iconDatabaseFile);

            // delete .pickle files and log files.

            ScanAndDeleteContents(imvuFolder, "*.pickle");
            ScanAndDeleteContents(imvuFolder, "*.log*");

            // imvu puts random stuff in temp folder aswell..
            ScanAndDeleteContents(tempFolder, "*", true);

            remainingFileCount = QueuedStats.NumberOfQueuedOperations;

            progressBar.Maximum = totalFileCount + remainingFileCount;
            progressBar.Value = totalFileCount;
            
            Application.DoEvents();

            DateTime now = DateTime.Now;
            DateTime estimateCompletion = DateTime.Now;

            double delta0 = remainingFileCount;
            double delta1 = remainingFileCount;
            TimeSpan delta_t = TimeSpan.Zero;

            // we wait until the queued operations have completed.
            while (QueuedStats.NumberOfQueuedOperations > 0)
            {
                Thread.Sleep(100);

                progressBar.Value = totalFileCount + (remainingFileCount - QueuedStats.NumberOfQueuedOperations);

                Application.DoEvents();

                // update second intervals
                if (now.AddSeconds(1) >= DateTime.Now)
                {
                    delta1 = QueuedStats.NumberOfQueuedOperations;
                    delta_t = DateTime.Now - now;

                    double persec = (1.0 * (delta0 - delta1)) / delta_t.TotalSeconds;

                    TimeSpan remaining = TimeSpan.FromSeconds(delta1 * persec);
                    estimateCompletion = DateTime.Now.Add(remaining);

                    if (remaining.Minutes > 0)
                        this.actionLabel.Text = "Deleting.. (" + remaining.Minutes.ToString() + "min " + remaining.Seconds.ToString() + "sec) remaining..";
                    else
                        this.actionLabel.Text = "Deleting.. (" + remaining.Seconds.ToString() + "sec) remaining..";

                    delta0 = delta1;
                    now = DateTime.Now;
                }


            }

            long afterFreeSpace = new System.IO.DriveInfo(driveletter).AvailableFreeSpace;
            long freed = Math.Abs(afterFreeSpace - totalFreeSpace);

            this.actionLabel.Text = "Status OK: " + totalFileCount.ToString() + " Files, " + FormatBytes(freed) + " freed. ";

            //ReportingService.WriteEvent("OK", "Cache has been cleaned");

            if (totalFileCount > 0 || freed > 0)
            {
                if (ReportingService.HasErrors)
                    MessageBox.Show(this, "Cache has been cleared, but there was some files that maybe couldn't be deleted, check the errors log for more information.\r\n " + totalFileCount.ToString() + " Files processed, " + FormatBytes(freed) + " freed. ", "Cache has been cleaned with errors", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                else
                    MessageBox.Show(this, totalFileCount.ToString() + " Files deleted, " + FormatBytes(freed) + " freed. ", "Cache has been cleaned", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
                MessageBox.Show(this, "Cache is empty", "Cache has been cleaned", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

            if (ReportingService.HasErrors)
            {
                System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo("errors.log");
                startInfo.UseShellExecute = true;

                System.Diagnostics.Process.Start(startInfo);
            }

            // we can close.
            log.Dispose();
            this.Close();
        }


        private void abortButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }

    }
}
